package com.game.commons.network;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

/**
 * NIO服务
 * @author 杜祥
 * @create 2013-6-3
 */
public class NioServer 
{
	private static Logger log = Logger.getLogger(NioServer.class);
	
	
	private final List<SelectionKey> serverChannelKeys = new ArrayList<SelectionKey>();
	
	
	/**
	 * 调度器
	 */
	private Dispatcher dispatcher;
	
	
	/**
	 * 用于多个调度器之间的均衡负载
	 */
	private int currentReadWriteDispatcher;
	
	
	/**
	 * 读写调度器
	 */
	private Dispatcher[] readWriteDispatcher;
	
	
	/**
	 * 执行关闭连接任务的线程池
	 */
	private final DisconnectionThreadPool dcPool;
	
	
	/**
	 * 读写线程的个数
	 */
	private int readWriteThreads;
	
	
	private ServerConfig[]	configs;
	
	
	
	/**
	 * 构造一个新的NioServer并设置读写线程数、关闭连接任务执行线程池、服务配置
	 * @param readWriteThreads
	 * @param dcPool
	 * @param configs
	 * @see com.game.commons.network.DisconnectionThreadPool
	 * @see com.game.commons.network.ServerConfig
	 */
	public NioServer(int readWriteThreads, DisconnectionThreadPool dcPool, ServerConfig... configs)
	{
		this.readWriteThreads = readWriteThreads;
		
		this.dcPool = dcPool;
		
		this.configs = configs;
	}
	
	
	public void connect()
	{
		try 
		{
			this.initDispatcher(readWriteThreads, dcPool);
			
			for(ServerConfig config : configs)
			{
				ServerSocketChannel socketChannel = ServerSocketChannel.open();
				
				socketChannel.configureBlocking(Boolean.FALSE);
				
				InetSocketAddress address;
				
				if("*".equals(config.hostName))
				{
					address = new InetSocketAddress(config.port);
					log.info("服务器启动监听端口:[" + config.port + "]。");
				}
				else
				{
					address = new InetSocketAddress(config.hostName, config.port);
					log.info("服务器启动监听IP[" + config.hostName + "]端口[" + config.port + "]。");
				}
				
				socketChannel.socket().bind(address);
				
				SelectionKey selectionKey = getDispatcher().register(socketChannel, SelectionKey.OP_ACCEPT, 
						new Acceptor(config.connectionFactory, this));
				this.serverChannelKeys.add(selectionKey);
			}
		} 
		catch (Exception e) 
		{
			log.error("服务器初始化失败！", e);
			throw new Error("服务器初始化失败！");
		}
	}
	
	
	
	/**
	 * 初始化调度器
	 * @param readWriteThreads			
	 * 						读写线程个数
	 * @param dcPool
	 * 						断开连接任务执行线程池
	 * @throws IOException 
	 */
	private void initDispatcher(int readWriteThreads, DisconnectionThreadPool dcPool) throws IOException
	{
		if(readWriteThreads <= 1)
		{
			dispatcher = new AcceptReadWriteDispatcherImpl("AcceptReadWrite Dispatcher", dcPool);
			dispatcher.start();
		}
		else
		{
			dispatcher = new AcceptDispatcherImpl("Accept Dispatcher");
			dispatcher.start();
			
			readWriteDispatcher = new Dispatcher[readWriteThreads];
			for(int i = 0; i < readWriteDispatcher.length; i++)
			{
				readWriteDispatcher[i] = new AcceptReadWriteDispatcherImpl("ReadWrite Dispatcher-" + i, dcPool);
				readWriteDispatcher[i].start();
			}
		}
	}


	final Dispatcher getDispatcher() 
	{
		return dispatcher;
	}
	
	
	/**
	 * 获得调度员<br>
	 * 当初始化时设置的readWriteThreads>0时，这个方法会均衡的来计算返回。
	 * @return
	 */
	public final Dispatcher getReadWriteDispatcher()
	{
		if(readWriteDispatcher == null)
			return dispatcher;
		
		if(readWriteDispatcher.length == 1)
			return readWriteDispatcher[0];
		
		if(currentReadWriteDispatcher >= readWriteDispatcher.length)
			currentReadWriteDispatcher = 0;
		
		return readWriteDispatcher[currentReadWriteDispatcher++];
	}
	
	/**
	 * 获得活跃的连接数
	 * @return
	 */
	public final int getActiveConnections()
	{
		int count = 0;
		
		if(readWriteDispatcher != null)
		{
			for(Dispatcher d : readWriteDispatcher)
			{
				count += d.selector().selectedKeys().size();
			}
		}
		else
		{
			count += getDispatcher().selector().selectedKeys().size() - this.serverChannelKeys.size();
		}
		
		return count;
	}
	
	
	/**
	 * 关闭方法
	 */
	public void shutdown()
	{
		
	}
}
